package com.tlgen.orm.factory;

import com.tlgen.orm.function.TypeFunction;
import com.tlgen.orm.model.QueryParams;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import static com.tlgen.orm.enums.Query.*;
import static com.tlgen.orm.utils.ReflectionUtils.getLambdaFieldName;

/**
 * 对外 Api 查询操作
 * @param <T>
 */
public class QueryOperator<T> {

    private List<QueryParams> paramsList = new ArrayList<>();

    public List<QueryParams> getParamsList() {
        return paramsList;
    }

    public void setParamsList(List<QueryParams> paramsList) {
        this.paramsList = paramsList;
    }

    public QueryOperator<T> eq(String column, Object value) {
        return getOperator(EQ.getValue(), column, value);
    }

    public QueryOperator<T> eq(TypeFunction<T, ?> fn, Object value) {
        return getOperator(EQ.getValue(), getLambdaFieldName(fn), value);
    }

    public QueryOperator<T> like(String column, Object value) {
        return getOperator(LIKE.getValue(), column, value);
    }

    public QueryOperator<T> like(TypeFunction<T, ?> fn, Object value) {
        return getOperator(LIKE.getValue(), getLambdaFieldName(fn), value);
    }

    public QueryOperator<T> notLike(String column, Object value) {
        return getOperator(NOT_LIKE.getValue(), column, value);
    }

    public QueryOperator<T> notLike(TypeFunction<T, ?> fn, Object value) {
        return getOperator(NOT_LIKE.getValue(), getLambdaFieldName(fn), value);
    }

    public QueryOperator<T> likeLeft(String column, Object value) {
        return getOperator(LIKE_LEFT.getValue(), column, value);
    }

    public QueryOperator<T> likeLeft(TypeFunction<T, ?> fn, Object value) {
        return getOperator(LIKE_LEFT.getValue(), getLambdaFieldName(fn), value);
    }

    public QueryOperator<T> likeRight(String column, Object value) {
        return getOperator(LIKE_RIGHT.getValue(), column, value);
    }

    public QueryOperator<T> likeRight(TypeFunction<T, ?> fn, Object value) {
        return getOperator(LIKE_RIGHT.getValue(), getLambdaFieldName(fn), value);
    }

    public QueryOperator<T> ne(String column, Object value) {
        return getOperator(NE.getValue(), column, value);
    }

    public QueryOperator<T> ne(TypeFunction<T, ?> fn, Object value) {
        return getOperator(NE.getValue(), getLambdaFieldName(fn), value);
    }

    public QueryOperator<T> or(String column, Object value) {
        return getOperator(OR.getValue(), column, value);
    }

    public QueryOperator<T> or(TypeFunction<T, ?> fn, Object value) {
        return getOperator(OR.getValue(), getLambdaFieldName(fn), value);
    }

    public QueryOperator<T> gt(String column, Object value) {
        return getOperator(GT.getValue(), column, value);
    }

    public QueryOperator<T> gt(TypeFunction<T, ?> fn, Object value) {
        return getOperator(GT.getValue(), getLambdaFieldName(fn), value);
    }

    public QueryOperator<T> ge(String column, Object value) {
        return getOperator(GE.getValue(), column, value);
    }

    public QueryOperator<T> ge(TypeFunction<T, ?> fn, Object value) {
        return getOperator(GE.getValue(), getLambdaFieldName(fn), value);
    }

    public QueryOperator<T> lt(String column, Object value) {
        return getOperator(LT.getValue(), column, value);
    }

    public QueryOperator<T> lt(TypeFunction<T, ?> fn, Object value) {
        return getOperator(LT.getValue(), getLambdaFieldName(fn), value);
    }

    public QueryOperator<T> le(String column, Object value) {
        return getOperator(LE.getValue(), column, value);
    }

    public QueryOperator<T> le(TypeFunction<T, ?> fn, Object value) {
        return getOperator(LE.getValue(), getLambdaFieldName(fn), value);
    }

    public QueryOperator<T> between(String column, Object start, Object end) {
        return getOperator(BETWEEN.getValue(), column, start, end);
    }

    public QueryOperator<T> between(TypeFunction<T, ?> fn, Object start, Object end) {
        return getOperator(BETWEEN.getValue(), getLambdaFieldName(fn), start, end);
    }

    public QueryOperator<T> notBetween(String column, Object start, Object end) {
        return getOperator(NOT_BETWEEN.getValue(), column, start, end);
    }

    public QueryOperator<T> notBetween(TypeFunction<T, ?> fn, Object start, Object end) {
        return getOperator(NOT_BETWEEN.getValue(), getLambdaFieldName(fn), start, end);
    }

    public QueryOperator<T> isNull(String column) {
        return getOperator(IS_NULL.getValue(), column, (Object) null);
    }

    public QueryOperator<T> isNull(TypeFunction<T, ?> fn) {
        return getOperator(IS_NULL.getValue(), getLambdaFieldName(fn), (Object) null);
    }

    public QueryOperator<T> isNotNull(String column) {
        return getOperator(IS_NOT_NULL.getValue(), column, (Object) null);
    }

    public QueryOperator<T> isNotNull(TypeFunction<T, ?> fn) {
        return getOperator(IS_NOT_NULL.getValue(), getLambdaFieldName(fn), (Object) null);
    }

    public QueryOperator<T> in(String column, Object value) {
        return getOperator(IN.getValue(), column, value);
    }

    public QueryOperator<T> in(TypeFunction<T, ?> fn, List<String> valueList) {
        return getOperator(IN.getValue(), getLambdaFieldName(fn), valueList);
    }

    public QueryOperator<T> notIn(String column, List<String> valueList) {
        return getOperator(NOT_IN.getValue(), column, valueList);
    }

    public QueryOperator<T> notIn(TypeFunction<T, ?> fn, List<String> valueList) {
        return getOperator(NOT_IN.getValue(), getLambdaFieldName(fn), valueList);
    }

    public QueryOperator<T> groupBy(String... columns) {
        return getOperator(GROUP_BY.getValue(), columns);
    }

    @SafeVarargs
    public final QueryOperator<T> groupBy(TypeFunction<T, ?>... fns) {
        return getOperator(GROUP_BY.getValue(), fns);
    }

    public QueryOperator<T> orderByAsc(String... columns) {
        return getOperator(ORDER_BY_ASC.getValue(), columns);
    }

    @SafeVarargs
    public final QueryOperator<T> orderByAsc(TypeFunction<T, ?>... fns) {
        return getOperator(ORDER_BY_ASC.getValue(), fns);
    }

    public QueryOperator<T> orderByDesc(String... columns) {
        return getOperator(ORDER_BYD_ESC.getValue(), columns);
    }

    @SafeVarargs
    public final QueryOperator<T> orderByDesc(TypeFunction<T, ?>... fns) {
        return getOperator(ORDER_BYD_ESC.getValue(), fns);
    }

    private <E> QueryOperator<E> getOperator(String operator, String column, Object value) {
        QueryOperator<E> queryOperator = new QueryOperator<>();
        QueryParams params = new QueryParams();
        params.setOperator(operator);
        params.setColumn(column);
        params.setValue(value);
        this.paramsList.add(params);
        queryOperator.setParamsList(paramsList);
        return queryOperator;
    }

    private <E> QueryOperator<E> getOperator(String operator, String column, Object start, Object end) {
        QueryOperator<E> queryOperator = new QueryOperator<>();
        QueryParams params = new QueryParams();
        params.setOperator(operator);
        params.setColumn(column);
        params.setValue(String.valueOf(start).concat("&&").concat(String.valueOf(end)));
        this.paramsList.add(params);
        queryOperator.setParamsList(paramsList);
        return queryOperator;
    }

    private <E> QueryOperator<E> getOperator(String operator, String... columns) {
        QueryOperator<E> queryOperator = new QueryOperator<>();
        QueryParams params = new QueryParams();
        params.setOperator(operator);
        params.setColumn(String.join(", ", Arrays.asList(columns)));
        params.setValue(null);
        this.paramsList.add(params);
        queryOperator.setParamsList(paramsList);
        return queryOperator;
    }

    private <E> QueryOperator<E> getOperator(String operator, TypeFunction<T, ?>... fns) {
        List<String> stringList = new ArrayList<>();
        for (TypeFunction<T, ?> fn : fns) {
            stringList.add(getLambdaFieldName(fn));
        }
        QueryOperator<E> queryOperator = new QueryOperator<>();
        QueryParams params = new QueryParams();
        params.setOperator(operator);
        params.setColumn(String.join(", ", stringList));
        params.setValue(null);
        this.paramsList.add(params);
        queryOperator.setParamsList(paramsList);
        return queryOperator;
    }

}
